97장. 도메인 설계 — 3개 서비스로 쪼개기
이 장에서 말하고자 하는 것
지금까지 우리는 AWS의 부품을 하나씩 살펴봤다.
이제 마지막 단원은
“한 서비스를 어떻게 마이크로서비스 3개로 쪼개는가”
를 구체적으로 다룬다.
이 장은 도메인 분리 의 기준과 결과를 정리한다.
1. 시작 — 모놀리스의 하루
작은 쇼핑 서비스를 가정하자.
사용자
├─ 회원가입 / 로그인
├─ 상품 둘러보기
├─ 장바구니
├─ 주문
├─ 결제
└─ 주문 내역 보기
처음에는 한 코드베이스 · 한 DB로 충분하다.
[모놀리스]
└─ RDS (users + products + orders + payments)
문제는 자라면서 시작된다.
- 결제 모듈 변경이 회원가입 배포와 묶임
- 한 팀이 다른 영역을 모르고 건드림
- DB 한 곳에 부하 누적
2. 분리의 기준 — 도메인 경계
마구잡이로 쪼개면 더 큰 혼란이 된다.
“독립적으로 변할 수 있는 단위” 로 나눈다
판단 기준:
- 데이터 소유권 — 누가 이 데이터의 진실을 책임지나
- 변경 주기 — 함께 자주 바뀌나, 따로 바뀌나
- 팀 / 책임 — 누가 운영하나
- 외부 의존성 — 외부 API · 정책에 묶이나
이 책에서는 다음 세 도메인으로 나눈다.
1. users (회원 · 인증)
2. orders (주문 · 장바구니)
3. payments (결제)
각각이 위 기준에서 독립적이다.
3. 각 서비스의 역할
users
- 회원가입 / 로그인 / 프로필
- JWT 발급 (Cognito 와 함께)
- DB: 사용자 테이블
orders
- 장바구니 · 주문 생성 · 주문 조회
- “사용자 정보” 는 API로 또는 이벤트로 받아 일부만 저장
- DB: 주문 / 장바구니 테이블
payments
- 결제 시도 · 환불 · 결제 내역
- 외부 PG와 통신
- DB: 결제 트랜잭션 (감사 강화)
“주문 데이터를 누가 책임지나” 같은 질문에 한 서비스만 답해야 한다
4. 데이터를 어떻게 공유할까
70장에서 본 세 가지 패턴.
orders 가 "사용자 이름" 이 필요할 때:
① 동기 호출: orders → users API
② 이벤트 + 로컬 복제:
users 가 "UserCreated/Updated" 발행
orders 가 받아서 자기 DB에 (id, name) 만 저장
③ 별도 BFF / CQRS
이 책의 척추 구조는 주로 ②를 권장한다.
orders DB 안에:
user_id_name_cache (id, name) — 화면용
실제 진실은 users 서비스
5. 서비스 간 호출
orders → users (사용자 확인) : 동기
orders → payments (결제 시도) : 동기 (응답 필요)
orders → SNS "OrderCreated" : 비동기
├─ notification 워커 → 알림
├─ analytics 워커 → 통계
└─ inventory 워커 → 재고 차감
- 사용자에게 응답이 필요한 흐름 → 동기
- 부수 효과 → 비동기
6. 같은 패턴, 다른 인스턴스
각 서비스가 같은 인프라 패턴을 반복한다.
서비스 = {
ECR 리포지토리
ECS Task Definition + Service
ALB Target Group + Listener Rule
IAM Task Role (좁은 권한)
RDS 또는 DynamoDB
Secrets Manager 항목
CloudWatch Log Group
CloudWatch Alarm 묶음
}
Terraform 모듈 하나로 추상화하면 새 서비스 추가가 단 몇 줄.
module "users" { source = "./modules/microservice" name = "users" ... }
module "orders" { source = "./modules/microservice" name = "orders" ... }
module "payments" { source = "./modules/microservice" name = "payments" ... }
7. 경계의 비용 — 분리는 공짜가 아니다
마이크로서비스는 다음 비용이 든다.
- 네트워크 호출 지연
- 분산 트랜잭션의 어려움 (Saga · 멱등성 · Outbox)
- 운영 도구의 추가 (관측성 · IAM · 배포)
- DB 데이터 중복 (의도적)
- 개발 환경 복잡도
작게 시작해서 경계가 보이는 곳부터 쪼개는 게 가장 안전하다
처음부터 5개 · 10개 서비스로 쪼개는 건 거의 항상 후회로 이어진다
8. 이 책에서 3개로 끝낸 이유
- 도메인 경계가 명확
- 한 권에서 다룰 수 있는 폭
- 한 사람 / 작은 팀이 운영 가능
진짜 서비스가 자라면 다음으로 자연스럽게 분리되는 후보들:
notification, search, recommendation, inventory, audit, ...
같은 패턴을 그대로 복제하면 된다.
9. 우리 서비스의 최종 도식
[사용자]
↓ DNS (Route 53)
[CloudFront] (WAF, us-east-1 ACM, OAC)
├─ /static/* → S3 static (CloudFront + OAC)
└─ /api/* → API Gateway (JWT Authorizer · Rate limit)
↓ VPC Link
[Private ALB] (서울 ACM)
├─ /api/users/* → ECS "users" → RDS users-db
├─ /api/orders/* → ECS "orders" → RDS orders-db
└─ /api/payments/* → ECS "payments" → RDS payments-db
부수 효과:
ECS "orders" → SNS "order-events"
├─ SQS notification → ECS notification-worker
├─ SQS analytics → ECS analytics-worker
└─ SQS inventory → ECS inventory-worker
이 그림이 1~96장의 결과물이고
다음 장(98) 이 그 흐름을 한 번 더 완성형으로 그린다.
10. 직접 점검 — 도메인 경계가 잘 잡혔는지
다음 질문에 명확히 답하면 분리가 잘된 것이다.
- 이 서비스의 데이터 진실은 누구인가
- 다른 서비스가 이 데이터를 어떻게 받는가 (API / 이벤트 / 복제)
- 이 서비스 혼자 배포할 수 있는가
- 이 서비스 장애가 다른 서비스에 어디까지 영향을 주는가
- 한 명이 이 서비스를 처음부터 끝까지 운영할 수 있는가
11. 코드로는 이렇게 생겼다 — 서비스 모듈 (스케치)
module "users" {
source = "./modules/microservice"
name = "users"
container_image = "${var.ecr_url}/users:${var.image_tag}"
container_port = 8080
cpu = 512
memory = 1024
desired_count = 2
alb_listener_arn = module.shared.alb_listener_arn
path_pattern = "/api/users/*"
db = {
engine = "postgres"
instance_class = "db.t4g.small"
multi_az = true
}
task_role_extra_policies = [
aws_iam_policy.users_sns_publish.arn,
]
}
- 모듈은 70~80줄의 Terraform
- 인스턴스마다 위처럼 10여 줄
12. 이렇게 쓰면 망한다 — 안티패턴
안티패턴 1. 기능 단위가 아닌 기술 계층으로 나눈다
“API 서비스 / 비즈니스 서비스 / DB 서비스” 식 분리는 결합도가 폭증한다.
도메인(사람이 알아보는 비즈니스 단위) 으로 나눈다
안티패턴 2. 작은 단위로 너무 잘게 쪼갠다
“마이크로” 가 아니라 “나노” 서비스 — 호출 지옥.
한 서비스가 한 화면의 한 영역 전체를 책임질 수 있게
안티패턴 3. 분리해 놓고 같은 DB를 공유
이름만 분리, 실질은 모놀리스.
Database per Service 가 진짜 분리
안티패턴 4. 처음부터 모든 부수 효과를 동기로 묶는다
한 서비스 장애가 모든 서비스로 번진다.
13. 한 줄로 정리
도메인 분리는 “독립적으로 변할 수 있는 단위” 로 나누는 일이며,
데이터 소유 · 변경 주기 · 책임 · 외부 의존이 그 기준이다
14. 이 장의 핵심 정리
- 도메인 경계가 명확한 곳부터 나눈다.
- 이 책은 users · orders · payments 의 3 서비스로 끝낸다.
- 같은 인프라 패턴을 Terraform 모듈로 추상화해 반복한다.
- 외부 응답은 동기, 부수 효과는 비동기.
- 분리는 비용이 든다 — 작게 시작하고 경계가 보일 때 쪼갠다.
- 다음 장은 이 세 서비스가 합쳐진 전체 흐름을 한 번 더 정리한다.